package com.duan.laugh.starter.minio.core;

import com.duan.laugh.starter.minio.vo.FileVO;
import io.minio.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import io.minio.messages.Upload;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * minio文件操作模板
 *
 * 名词解释
 *  - bucket：存储桶，类似于文件夹
 *  - file：文件，放在存储桶中
 *
 * @author duanjw
 */
@NoArgsConstructor
@AllArgsConstructor
public class MinioTemplate{
    private MinioClient minioClient;
    /**
     * 新增bucket
     *
     * @param bucketName bucket名称
     */
    @SneakyThrows
    public void insertBucket(String bucketName) {
        // bucket不存在则创建
        if (!minioClient.bucketExists(bucketName)) {
            minioClient.makeBucket(bucketName);
        }
    }
    /**
     * 根据bucketName查看
     *
     * @param bucketName bucket名称
     */
    @SneakyThrows
    public Optional<Bucket> getBucket(String bucketName) {
        return minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
    }
    /**
     * 查询bucket
     *
     */
    @SneakyThrows
    public List<Bucket> selectBucket() {
        return minioClient.listBuckets();
    }


    /**
     * 删除bucket
     *
     * @param bucketName bucket名称
     */
    @SneakyThrows
    public void deleteBucket(String bucketName) {
        minioClient.removeBucket(bucketName);
    }

    /**
     * 根据文件前置查询文件
     *
     * @param bucketName bucket名称
     * @param prefix     前缀
     * @param recursive  是否递归查询
     * @return Item 列表
     */
    @SneakyThrows
    public List<Item> selectFileByPrefix(String bucketName, String prefix, boolean recursive) {
        Iterable<Result<Item>> objectsIterator = minioClient
                .listObjects(bucketName, prefix, recursive);
        List<Item> itemList = new ArrayList<>();
        for (Result<Item> result : objectsIterator) {
            itemList.add(result.get());
        }
       return itemList;
    }
    /**
     * 根据bucket获取上传未完成的文件
     *
     * @param bucketName bucket名称
     * @return Item 列表
     */
    @SneakyThrows
    public List<Upload> selectIncompleteByBucket(String bucketName) {
        Iterable<Result<Upload>> uploads = minioClient.listIncompleteUploads(bucketName);
        List<Upload> uploadList = new ArrayList<>();
        for (Result<Upload> result : uploads) {
            uploadList.add(result.get());
        }
        return uploadList;
    }

    /**
     * 获取文件的分享地址，分享地址为get请求
     *
     * 生成一个给HTTP GET请求用的presigned URL。
     * 浏览器/移动端的客户端可以用这个URL进行下载，即使其所在的存储桶是私有的。
     * 这个presigned URL可以设置一个失效时间，默认值是7天。
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param expires    失效时间（以秒为单位），默认是7天，不得大于七天
     * @return url
     */
    @SneakyThrows
    public String getShareURL(String bucketName, String objectName, Integer expires) {
        return minioClient.presignedGetObject(bucketName, objectName, expires);
    }

    /**
     * 获取文件的分享地址，分享地址为Put请求
     *
     * 生成一个给HTTP PUT请求用的presigned URL。
     * 浏览器/移动端的客户端可以用这个URL进行下载，即使其所在的存储桶是私有的。
     * 这个presigned URL可以设置一个失效时间，默认值是7天。
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param expires    失效时间（以秒为单位），默认是7天，不得大于七天
     * @return url
     */
    @SneakyThrows
    public String getPutShareURL(String bucketName, String objectName, Integer expires) {
        return minioClient.presignedPutObject(bucketName, objectName, expires);
    }

    /**
     * 获取文件的分享地址，分享地址为Put请求
     *
     * 生成一个给HTTP GET请求用的presigned URL。
     * 浏览器/移动端的客户端可以用这个URL进行下载，即使其所在的存储桶是私有的。
     * 这个presigned URL可以设置一个失效时间，默认值是7天。
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param expires    失效时间（以秒为单位），默认是7天，不得大于七天
     * @return url
     */
    @SneakyThrows
    public String getPostShareURL(String bucketName, String objectName, Integer expires) {
        PostPolicy policy = new PostPolicy(bucketName, objectName,
                DateTime.now().plusSeconds(expires));
        Map<String,String> formData = minioClient.presignedPostPolicy(policy);
        for (Map.Entry<String,String> entry : formData.entrySet()) {
            return entry.getValue();
        }
        return null;
    }

    /**
     * 获取文件二进制流
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @return 二进制流
     */
    @SneakyThrows
    public InputStream getFile(String bucketName, String objectName) {
        return minioClient.getObject(bucketName, objectName);
    }

    /**
     * 上传文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param stream     文件流
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
     */
    public void uploadFile(String bucketName, String objectName, InputStream stream) throws Exception {
        minioClient.putObject(bucketName, objectName, stream, (long) stream.available(), null, null, "application/octet-stream");
    }

    /**
     * 上传文件
     *
     * @param bucketName  bucket名称
     * @param objectName  文件名称
     * @param stream      文件流
     * @param size        大小
     * @param contextType 类型
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
     */
    public void uploadFile(String bucketName, String objectName, InputStream stream, long size, String contextType) throws Exception {
        minioClient.putObject(bucketName, objectName, stream, size, null, null, contextType);
    }
    /**
     * 获取文件信息
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#statObject
     */
    public FileVO getFileInfo(String bucketName, String objectName) throws Exception {
        return new FileVO(minioClient.statObject(bucketName, objectName));
    }

    /**
     * 删除文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#removeObject
     */
    public void deleteFile(String bucketName, String objectName) throws Exception {
        minioClient.removeObject(bucketName, objectName);
    }

    /**
     * 复制文件
     * @param bucketName
     * @param filename
     * @param targetBucketName
     * @param targetFilename
     */
    @SneakyThrows
    public void copyFile(String bucketName, String filename, String targetBucketName, String targetFilename) {
        minioClient.copyObject(bucketName, filename, targetBucketName, targetFilename);
    }
    /**
     * 复制文件
     * @param bucketName
     * @param filename
     * @param targetBucketName
     * @param targetFilename
     * @param cpConds
     * @param metadata
     */
    @SneakyThrows
    public void copyFile(String bucketName, String filename, String targetBucketName, String targetFilename, CopyConditions cpConds, Map<String, String> metadata) {
        minioClient.copyObject(bucketName, filename, targetBucketName, targetFilename, cpConds, metadata);
    }

}
